using System;

using LumiSoft.Net;
using LumiSoft.Net.Mime;

namespace LumiSoft.Net.IMAP.Server
{
	/// <summary>
	/// IMAP search key (RFC 3501 6.4.4 SEARCH Command).
	/// </summary>
	internal class SearchKey
	{
		private string m_SearchKeyName  = "";
		private object m_SearchKeyValue = null;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public SearchKey(string searchKeyName,object value)
		{
			m_SearchKeyName = searchKeyName;
			m_SearchKeyValue = value;
		}


		#region method Parse

		/// <summary>
		/// Parses one search key from current position. Returns null if there isn't any search key left.
		/// </summary>
		/// <param name="reader"></param>
		public static SearchKey Parse(StringReader reader)
		{
			string searchKeyName  = "";
			object searchKeyValue = null;

			//Remove spaces from string start
			reader.ReadToFirstChar();

			// Search keyname is always 1 word
            string word = reader.ReadWord();
			if(word == null){
				return null;
			}
			word = word.ToUpper().Trim();

			//Remove spaces from string start
			reader.ReadToFirstChar();

			#region ALL

			// ALL
			//		All messages in the mailbox; the default initial key for ANDing.
			if(word == "ALL"){
				searchKeyName = "ALL";
			}

			#endregion

			#region ANSWERED

			// ANSWERED
			//		Messages with the \Answered flag set.
			else if(word == "ANSWERED"){
				// We internally use KEYWORD ANSWERED
				searchKeyName = "KEYWORD";
				searchKeyValue = "ANSWERED";
			}

			#endregion

			#region BCC

			// BCC <string>
			//		Messages that contain the specified string in the envelope structure's BCC field.
			else if(word == "BCC"){
				// We internally use HEADER "BCC:" "value"
				searchKeyName = "HEADER";

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{"BCC:",TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("BCC <string> value is missing !");
				}
			}

			#endregion

			#region BEFORE

			//	BEFORE <date>
			//		Messages whose internal date (disregarding time and timezone) is earlier than the specified date.
			else if(word == "BEFORE"){
				searchKeyName = "BEFORE";

				// Read <date>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid BEFORE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("BEFORE <date> value is missing !");
				}
			}

			#endregion

			#region BODY

			//	BODY <string>
			//		Messages that contain the specified string in the body of the message.
			else if(word == "BODY"){
				searchKeyName = "BODY";

				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = val;
				}
				else{
					throw new Exception("BODY <string> value is missing !");
				}
			}

			#endregion

			#region CC

			//	CC <string>
			//		Messages that contain the specified string in the envelope structure's CC field.
			else if(word == "CC"){
				// We internally use HEADER "CC:" "value"
				searchKeyName = "HEADER";

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{"CC:",TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("CC <string> value is missing !");
				}
			}

			#endregion

			#region DELETED

			// DELETED
			//		Messages with the \Deleted flag set.
			else if(word == "DELETED"){
				// We internally use KEYWORD DELETED
				searchKeyName = "KEYWORD";
				searchKeyValue = "DELETED";
			}

			#endregion

			#region DRAFT

			//	DRAFT
			//		Messages with the \Draft flag set.
			else if(word == "DRAFT"){
				// We internally use KEYWORD DRAFT
				searchKeyName = "KEYWORD";
				searchKeyValue = "DRAFT";
			}

			#endregion

			#region FLAGGED

			//	FLAGGED
			//		Messages with the \Flagged flag set.
			else if(word == "FLAGGED"){
				// We internally use KEYWORD FLAGGED
				searchKeyName = "KEYWORD";
				searchKeyValue = "FLAGGED";
			}

			#endregion

			#region FROM

			//	FROM <string>
			//		Messages that contain the specified string in the envelope structure's FROM field.
			else if(word == "FROM"){
				// We internally use HEADER "FROM:" "value"
				searchKeyName = "HEADER";

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{"FROM:",TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("FROM <string> value is missing !");
				}
			}

			#endregion

			#region HEADER

			//	HEADER <field-name> <string>
			//		Messages that have a header with the specified field-name (as
			//		defined in [RFC-2822]) and that contains the specified string
			//		in the text of the header (what comes after the colon).  If the
			//		string to search is zero-length, this matches all messages that
			//		have a header line with the specified field-name regardless of
			//		the contents.
			else if(word == "HEADER"){
				searchKeyName = "HEADER";

				// Read <field-name>
				string fieldName = ReadString(reader);
				if(fieldName != null){
					fieldName = TextUtils.UnQuoteString(fieldName);
				}
				else{
					throw new Exception("HEADER <field-name> value is missing !");
				}

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{fieldName,TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("(HEADER <field-name>) <string> value is missing !");
				}
			}

			#endregion

			#region KEYWORD

			//	KEYWORD <flag>
			//		Messages with the specified keyword flag set.
			else if(word == "KEYWORD"){
				searchKeyName = "KEYWORD";

				// Read <flag>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					searchKeyValue = TextUtils.UnQuoteString(val);
				}
				else{
					throw new Exception("KEYWORD <flag> value is missing !");
				}
			}

			#endregion

			#region LARGER

			//	LARGER <n>
			//		Messages with an [RFC-2822] size larger than the specified number of octets.
			else if(word == "LARGER"){
				searchKeyName = "LARGER";

				// Read <n>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse <n> - must be integer value
					try{
						searchKeyValue = Convert.ToInt64(TextUtils.UnQuoteString(val));
					}
					// Invalid <n>
					catch{
						throw new Exception("Invalid LARGER <n> value '" + val + "', it must be numeric value !");
					}
				}
				else{
					throw new Exception("LARGER <n> value is missing !");
				}
			}

			#endregion

			#region NEW

			//	NEW
			//		Messages that have the \Recent flag set but not the \Seen flag.
			//		This is functionally equivalent to "(RECENT UNSEEN)".
			else if(word == "NEW"){
				// We internally use KEYWORD RECENT
				searchKeyName = "KEYWORD";
				searchKeyValue = "RECENT";
			}

			#endregion

			#region NOT

			//	NOT <search-key> or (<search-key> <search-key> ...)(SearchGroup)
			//		Messages that do not match the specified search key.
			else if(word == "NOT"){
				searchKeyName = "NOT";

				object searchItem = SearchGroup.ParseSearchKey(reader);
				if(searchItem != null){
					searchKeyValue = searchItem;
				}
				else{
					throw new Exception("Required NOT <search-key> isn't specified !");
				}
			}

			#endregion

			#region OLD

			//	OLD
			//		Messages that do not have the \Recent flag set.  This is
			//		functionally equivalent to "NOT RECENT" (as opposed to "NOT	NEW").
			else if(word == "OLD"){
				// We internally use UNKEYWORD RECENT
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "RECENT";
			}

			#endregion

			#region ON

			//	ON <date>
			//		Messages whose internal date (disregarding time and timezone) is within the specified date.
			else if(word == "ON"){
				searchKeyName = "ON";

				// Read <date>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid ON <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("ON <date> value is missing !");
				}
			}

			#endregion

			#region OR

			//	OR <search-key1> <search-key2> - SearckKey can be parenthesis list of keys !
			//		Messages that match either search key.
			else if(word == "OR"){
				searchKeyName = "OR";

				//--- <search-key1> ----------------------------------------------------//
				object searchKey1 = SearchGroup.ParseSearchKey(reader);
				if(searchKey1 == null){
					throw new Exception("Required OR <search-key1> isn't specified !");
				}
				//----------------------------------------------------------------------//

				//--- <search-key2> ----------------------------------------------------//
				object searchKey2 = SearchGroup.ParseSearchKey(reader);
				if(searchKey2 == null){
					throw new Exception("Required (OR <search-key1>) <search-key2> isn't specified !");
				}
				//-----------------------------------------------------------------------//

				searchKeyValue = new object[]{searchKey1,searchKey2};
			}

			#endregion

			#region RECENT

			//	RECENT
			//		Messages that have the \Recent flag set.
			else if(word == "RECENT"){
				// We internally use KEYWORD RECENT
				searchKeyName = "KEYWORD";
				searchKeyValue = "RECENT";
			}

			#endregion

			#region SEEN

			//	SEEN
			//		Messages that have the \Seen flag set.
			else if(word == "SEEN"){
				// We internally use KEYWORD SEEN
				searchKeyName = "KEYWORD";
				searchKeyValue = "SEEN";
			}

			#endregion

			#region SENTBEFORE

			//	SENTBEFORE <date>
			//		Messages whose [RFC-2822] Date: header (disregarding time and
			//		timezone) is earlier than the specified date.
			else if(word == "SENTBEFORE"){
				searchKeyName = "SENTBEFORE";

				// Read <date>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid SENTBEFORE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("SENTBEFORE <date> value is missing !");
				}
			}

			#endregion

			#region SENTON

			//	SENTON <date>
			//		Messages whose [RFC-2822] Date: header (disregarding time and
			//		timezone) is within the specified date.
			else if(word == "SENTON"){
				searchKeyName = "SENTON";

				// Read <date>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid SENTON <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("SENTON <date> value is missing !");
				}
			}

			#endregion

			#region SENTSINCE

			//	SENTSINCE <date>
			//		Messages whose [RFC-2822] Date: header (disregarding time and
			//		timezone) is within or later than the specified date.
			else if(word == "SENTSINCE"){
				searchKeyName = "SENTSINCE";

				// Read <date>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid SENTSINCE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("SENTSINCE <date> value is missing !");
				}
			}

			#endregion

			#region SINCE

			//	SINCE <date>
			//		Messages whose internal date (disregarding time and timezone)
			//		is within or later than the specified date.
			else if(word == "SINCE"){
				searchKeyName = "SINCE";

				// Read <date>
				string val = reader.ReadWord();
				if(val != null){
					// Parse date
					try{						
						searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val));
					}
					// Invalid date
					catch{
						throw new Exception("Invalid SINCE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !");
					}
				}
				else{
					throw new Exception("SINCE <date> value is missing !");
				}
			}

			#endregion

			#region SMALLER

			//	SMALLER <n>
			//		Messages with an [RFC-2822] size smaller than the specified number of octets.
			else if(word == "SMALLER"){
				searchKeyName = "SMALLER";

				// Read <n>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					val = TextUtils.UnQuoteString(val);

					// Parse <n> - must be integer value
					try{
						searchKeyValue = Convert.ToInt64(val);
					}
					// Invalid <n>
					catch{
						throw new Exception("Invalid SMALLER <n> value '" + val + "', it must be numeric value !");
					}
				}
				else{
					throw new Exception("SMALLER <n> value is missing !");
				}
			}

			#endregion

			#region SUBJECT

			//	SUBJECT <string>
			//		Messages that contain the specified string in the envelope structure's SUBJECT field.
			else if(word == "SUBJECT"){
				// We internally use HEADER "SUBJECT:" "value"
				searchKeyName = "HEADER";

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{"SUBJECT:",TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("SUBJECT <string> value is missing !");
				}
			}

			#endregion

			#region TEXT

			//	TEXT <string>
			//		Messages that contain the specified string in the header or body of the message.
			else if(word == "TEXT"){
				searchKeyName = "TEXT";

				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = val;
				}
				else{
					throw new Exception("TEXT <string> value is missing !");
				}
			}

			#endregion

			#region TO

			//	TO <string>
			//		Messages that contain the specified string in the envelope structure's TO field.
			else if(word == "TO"){
				// We internally use HEADER "TO:" "value"
				searchKeyName = "HEADER";

				// Read <string>
				string val = ReadString(reader);
				if(val != null){
					searchKeyValue = new string[]{"TO:",TextUtils.UnQuoteString(val)};
				}
				else{
					throw new Exception("TO <string> value is missing !");
				}
			}

			#endregion

			#region UID

			//	UID <sequence set>
			//		Messages with unique identifiers corresponding to the specified
			//		unique identifier set.  Sequence set ranges are permitted.
			else if(word == "UID"){
				searchKeyName = "UID";

				// Read <sequence set>
				string val = reader.QuotedReadToDelimiter(' ');				

				if(val != null){
					try{
						IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet();
						sequenceSet.Parse(TextUtils.UnQuoteString(val),long.MaxValue);

						searchKeyValue = sequenceSet;
					}
					catch{
						throw new Exception("Invalid UID <sequence-set> value '" + val + "' !");
					}
				}
				else{
					throw new Exception("UID <sequence-set> value is missing !");
				}
			}

			#endregion

			#region UNANSWERED

			//	UNANSWERED
			//		Messages that do not have the \Answered flag set.
			else if(word == "UNANSWERED"){
				// We internally use UNKEYWORD SEEN
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "ANSWERED";
			}

			#endregion

			#region UNDELETED

			//	UNDELETED
			//		Messages that do not have the \Deleted flag set.
			else if(word == "UNDELETED"){
				// We internally use UNKEYWORD UNDELETED
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "DELETED";
			}

			#endregion

			#region UNDRAFT

			//	UNDRAFT
			//		Messages that do not have the \Draft flag set.
			else if(word == "UNDRAFT"){
				// We internally use UNKEYWORD UNDRAFT
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "DRAFT";
			}

			#endregion

			#region UNFLAGGED

			//	UNFLAGGED
			//		Messages that do not have the \Flagged flag set.
			else if(word == "UNFLAGGED"){
				// We internally use UNKEYWORD UNFLAGGED
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "FLAGGED";
			}

			#endregion

			#region UNKEYWORD

			//	UNKEYWORD <flag>
			//		Messages that do not have the specified keyword flag set.
			else if(word == "UNKEYWORD"){
				searchKeyName = "UNKEYWORD";

				// Read <flag>
				string val = reader.QuotedReadToDelimiter(' ');
				if(val != null){
					searchKeyValue = TextUtils.UnQuoteString(val);
				}
				else{
					throw new Exception("UNKEYWORD <flag> value is missing !");
				}
			}

			#endregion

			#region UNSEEN

			//	UNSEEN
			//		Messages that do not have the \Seen flag set.
			else if(word == "UNSEEN"){
				// We internally use UNKEYWORD UNSEEN
				searchKeyName = "UNKEYWORD";
				searchKeyValue = "SEEN";
			}

			#endregion

			#region Unknown or SEQUENCESET

			// Unkown keyword or <sequence set>
			else{
				// DUMMY palce(bad design) in IMAP.
				// Active keyword can be <sequence set> or bad keyword, there is now way to distinguish what is meant.
				// Why they don't key work SEQUENCESET <sequence set> ?  

				// <sequence set>
				//		Messages with message sequence numbers corresponding to the
				//		specified message sequence number set.

				// Just try if it can be parsed as sequence-set
				try{
					IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet();
					sequenceSet.Parse(word,long.MaxValue);

					searchKeyName = "SEQUENCESET";
					searchKeyValue = sequenceSet;
				}
				// This isn't vaild sequnce-set value
				catch{
					throw new Exception("Invalid search key or <sequnce-set> value '" + word + "' !");
				}
			}

			#endregion
			
			// REMOVE ME:
		//	Console.WriteLine(searchKeyName + " : " + Convert.ToString(searchKeyValue));

			return new SearchKey(searchKeyName,searchKeyValue);
		}

		#endregion


		// TODO: We have envelope, see if Header is needed or can use envelope for it

		#region method IsHeaderNeeded

		/// <summary>
		/// Gets if message Header is needed for matching.
		/// </summary>
		/// <returns></returns>
		public bool IsHeaderNeeded()
		{
			if(m_SearchKeyName == "HEADER"){
				return true;
			}
			else if(m_SearchKeyName == "NOT"){
				return SearchGroup.IsHeaderNeededForKey(m_SearchKeyValue);
			}
			else if(m_SearchKeyName == "OR"){
				object serachKey1 = ((object[])m_SearchKeyValue)[0];
				object serachKey2 = ((object[])m_SearchKeyValue)[1];

				if(SearchGroup.IsHeaderNeededForKey(serachKey1) || SearchGroup.IsHeaderNeededForKey(serachKey2)){
					return true;
				}
			}
			else if(m_SearchKeyName == "SENTBEFORE"){
				return true;
			}
			else if(m_SearchKeyName == "SENTON"){
				return true;
			}
			else if(m_SearchKeyName == "SENTSINCE"){
				return true;
			}
			else if(m_SearchKeyName == "TEXT"){
				return true;
			}

			return false;
		}

		#endregion

		#region method IsBodyTextNeeded

		/// <summary>
		/// Gets if message body text is needed for matching.
		/// </summary>
		/// <returns></returns>
		public bool IsBodyTextNeeded()
		{
			if(m_SearchKeyName == "BODY"){
				return true;
			}
			else if(m_SearchKeyName == "NOT"){
				return SearchGroup.IsBodyTextNeededForKey(m_SearchKeyValue);
			}
			else if(m_SearchKeyName == "OR"){
				object serachKey1 = ((object[])m_SearchKeyValue)[0];
				object serachKey2 = ((object[])m_SearchKeyValue)[1];

				if(SearchGroup.IsBodyTextNeededForKey(serachKey1) || SearchGroup.IsBodyTextNeededForKey(serachKey2)){
					return true;
				}
			}
			else if(m_SearchKeyName == "TEXT"){
				return true;
			}

			return false;
		}

		#endregion

		#region method Match

		/// <summary>
		/// Gets if specified message matches with this class search-key.
		/// </summary>
		/// <param name="no">IMAP message sequence number.</param>
		/// <param name="uid">IMAP message UID.</param>
		/// <param name="size">IMAP message size in bytes.</param>
		/// <param name="internalDate">IMAP message INTERNALDATE (dateTime when server stored message).</param>
		/// <param name="flags">IMAP message flags.</param>
		/// <param name="mime">Mime message main header only.</param>
		/// <param name="bodyText">Message body text.</param>
		/// <returns></returns>
		public bool Match(long no,long uid,long size,DateTime internalDate,IMAP_MessageFlags flags,LumiSoft.Net.Mime.Mime mime,string bodyText)
		{
			#region ALL

			// ALL
			//		All messages in the mailbox; the default initial key for ANDing.
			if(m_SearchKeyName == "ALL"){
				return true;
			}

			#endregion

			#region BEFORE

			// BEFORE <date>
			//	Messages whose internal date (disregarding time and timezone)
			//	is earlier than the specified date.
			else if(m_SearchKeyName == "BEFORE"){
				if(internalDate.Date < (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region BODY

			// BODY <string>
			//	Messages that contain the specified string in the body of the message.
			//
			//	NOTE: Compare must be done on decoded header and decoded body of message.
			//		  In all search keys that use strings, a message matches the key if
			//		  the string is a substring of the field.  The matching is case-insensitive.
			else if(m_SearchKeyName == "BODY"){
				string val = bodyText;
				if(val != null && val.ToLower().IndexOf(((string)m_SearchKeyValue).ToLower()) > -1){
					return true;
				}
			}

			#endregion

			#region HEADER

			// HEADER <field-name> <string>
			//	Messages that have a header with the specified field-name (as
			//	defined in [RFC-2822]) and that contains the specified string
			//	in the text of the header (what comes after the colon).  If the
			//	string to search is zero-length, this matches all messages that
			//	have a header line with the specified field-name regardless of
			//	the contents.
			//
			//	NOTE: Compare must be done on decoded header field value.
			//		  In all search keys that use strings, a message matches the key if
			//		  the string is a substring of the field.  The matching is case-insensitive.
			else if(m_SearchKeyName == "HEADER"){				
				string[] headerField_value = (string[])m_SearchKeyValue;

                // If header field name won't end with :, add it
                if(!headerField_value[0].EndsWith(":")){
                    headerField_value[0] = headerField_value[0] + ":";
                }

				if(mime.MainEntity.Header.Contains(headerField_value[0])){
					if(headerField_value[1].Length == 0){
						return true;
					}
					else if(mime.MainEntity.Header.GetFirst(headerField_value[0]).Value.ToLower().IndexOf(headerField_value[1].ToLower()) > -1){
						return true;
					}
				}
			}

			#endregion

			#region KEYWORD

			// KEYWORD <flag>
			//	Messages with the specified keyword flag set.
			else if(m_SearchKeyName == "KEYWORD"){
				if((flags & IMAP_Utils.ParseMessageFlags((string)m_SearchKeyValue)) != 0){
					return true;
				}
			}

			#endregion
				
			#region LARGER
	
			// LARGER <n>
			//	Messages with an [RFC-2822] size larger than the specified number of octets.
			else if(m_SearchKeyName == "LARGER"){
				if(size > (long)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region NOT

			//	NOT <search-key> or (<search-key> <search-key> ...)(SearchGroup)
			//		Messages that do not match the specified search key.
			else if(m_SearchKeyName == "NOT"){
				return !SearchGroup.Match_Key_Value(m_SearchKeyValue,no,uid,size,internalDate,flags,mime,bodyText);
			}

			#endregion

			#region ON

			// ON <date>
			//	Messages whose internal date (disregarding time and timezone)
			//	is within the specified date.
			else if(m_SearchKeyName == "ON"){
				if(internalDate.Date == (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region OR

			//	OR <search-key1> <search-key2> - SearckKey can be parenthesis list of keys !
			//		Messages that match either search key.
			else if(m_SearchKeyName == "OR"){
				object serachKey1 = ((object[])m_SearchKeyValue)[0];
				object serachKey2 = ((object[])m_SearchKeyValue)[1];

				if(SearchGroup.Match_Key_Value(serachKey1,no,uid,size,internalDate,flags,mime,bodyText) || SearchGroup.Match_Key_Value(serachKey2,no,uid,size,internalDate,flags,mime,bodyText)){
					return true;
				}
			}

			#endregion

			#region SENTBEFORE

			// SENTBEFORE <date>
			//	Messages whose [RFC-2822] Date: header (disregarding time and
			//	timezone) is earlier than the specified date.
			else if(m_SearchKeyName == "SENTBEFORE"){				
				if(mime.MainEntity.Date.Date < (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region SENTON

			// SENTON <date>
			//	Messages whose [RFC-2822] Date: header (disregarding time and
			//	timezone) is within the specified date.
			else if(m_SearchKeyName == "SENTON"){				
				if(mime.MainEntity.Date.Date == (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region SENTSINCE

			// SENTSINCE <date>
			//	Messages whose [RFC-2822] Date: header (disregarding time and
			//	timezone) is within or later than the specified date.
			else if(m_SearchKeyName == "SENTSINCE"){
				if(mime.MainEntity.Date.Date >= (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region SINCE

			// SINCE <date>
			//	Messages whose internal date (disregarding time and timezone)
			//	is within or later than the specified date.	
			else if(m_SearchKeyName == "SINCE"){
				if(internalDate.Date >= (DateTime)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region SMALLER

			// SMALLER <n>
			//	Messages with an [RFC-2822] size smaller than the specified	number of octets.
			else if(m_SearchKeyName == "SMALLER"){
				if(size < (long)m_SearchKeyValue){
					return true;
				}
			}

			#endregion

			#region TEXT

			// TEXT <string>
			//	Messages that contain the specified string in the header or	body of the message.
			//
			//  NOTE: Compare must be done on decoded header and decoded body of message.
			//		  In all search keys that use strings, a message matches the key if
			//		  the string is a substring of the field.  The matching is case-insensitive.
			else if(m_SearchKeyName == "TEXT"){
				// See body first
				string val = bodyText;
				if(val != null && val.ToLower().IndexOf(((string)m_SearchKeyValue).ToLower()) > -1){
					return true;
				}

				// If we reach so far, that means body won't contain specified text and we need to check header.
				foreach(HeaderField headerField in mime.MainEntity.Header){
					if(headerField.Value.ToLower().IndexOf(((string)m_SearchKeyValue).ToLower()) > -1){
						return true;
					}
				}
			}

			#endregion

			#region UID

			// UID <sequence set>
			//	Messages with unique identifiers corresponding to the specified
			//	unique identifier set.  Sequence set ranges are permitted.
			else if(m_SearchKeyName == "UID"){
				return ((IMAP_SequenceSet)m_SearchKeyValue).Contains(uid);
			}

			#endregion

			#region UNKEYWORD

			// UNKEYWORD <flag>
			//	Messages that do not have the specified keyword flag set.
			else if(m_SearchKeyName == "UNKEYWORD"){
				if((flags & IMAP_Utils.ParseMessageFlags((string)m_SearchKeyValue)) == 0){
					return true;
				}
			}

			#endregion

			#region SEQUENCESET

			// <sequence set>
			//		Messages with message sequence numbers corresponding to the
			//		specified message sequence number set.
			else if(m_SearchKeyName == "SEQUENCESET"){
				return ((IMAP_SequenceSet)m_SearchKeyValue).Contains(no);
			}

			#endregion

			return false;
		}

		#endregion


		#region method ReadString

		/// <summary>
		/// Reads search-key &lt;string&gt; value.
		/// </summary>
		/// <param name="reader"></param>
		/// <returns></returns>
		private static string ReadString(StringReader reader)
		{
			//Remove spaces from string start
			reader.ReadToFirstChar();

			// We must support:
			//	word
			//  "text"
			//	{string_length}data(string_length)

			// {string_length}data(string_length)
			if(reader.StartsWith("{")){
				// Remove {
				reader.ReadSpecifiedLength("{".Length);

				int dataLength = Convert.ToInt32(reader.QuotedReadToDelimiter('}'));
				return reader.ReadSpecifiedLength(dataLength);
			}
			
			return TextUtils.UnQuoteString(reader.QuotedReadToDelimiter(' '));
		}

		#endregion

	}
}
